Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
dom-render
Advanced tools
<script src="https://cdn.jsdelivr.net/npm/dom-render@1.0.91/dist/bundle.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body id="app">
${this.name}$
<script src="https://cdn.jsdelivr.net/npm/dom-render@1.0.86/dist/bundle.js"></script>
<script>
let data = {
name: 'my name is dom-render'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
</html>
<body id="app">
${this.name}$ <!-- outout: <i>my name is dom-render</i> -->
#{this.name}# <!-- outout text is italic: my name is dom-render -->
<script>
let data = {
name: '<i>my name is dom-render</i>'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
<body id="app">
<input type="text" value="${this.name}$" style="${'color: '+this.color}$">
<button dr-event-click="this.changeData();">change</button>
</body>
class Data {
name = 'my name is dom-render';
color = '#ff0000';
changeData() {
this.name = RandomUtils.getRandomString(10);
this.color = RandomUtils.getRandomColor();
}
}
const data = DomRender.run(new Data(), document.querySelector('#app')!);
<body id="app">
<div dr-if="true">true</div> <!-- render -->
<div dr-if="this.gender === 'M'">gender: M</div> <!-- No Render -->
<script>
let data = {
gender: 'F'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
<div dr-for="var i = 0; i < this.friends.length; i++"> friend</div>
<div dr-for-of="this.friends"> ${#it#.name}$</div>
<div dr-for-of="$range(10, 20)"><div>${#it#}$</div><div>
<div dr-for="var i = 1 ; i <= 9 ; i++" dr-it="i">
${#it#}$ *
<scope dr-for="var y = 1 ; y <= 9 ; y++" dr-it="y" dr-var="superIt=#it#" dr-strip="true">
#it# = ${var.superIt * #it#}$
</scope>
</div>
<h3>appender</h3>
<ul>
<li dr-appender="@this@.appender">
${#it#}$
</li>
</ul>
<button dr-event-click="@this@.append()">appending</button>
<button dr-event-click="@this@.modifyAppender(0)">idx 0 modify</button>
<button dr-event-click="@this@.clearAppend()">appender clear</button>
class Data {
appender = new Appender();
constructor() {
this.appender.push('init' + RandomUtils.uuid(), 'init' + RandomUtils.uuid());
}
append() {
this.appender.push(RandomUtils.uuid(), RandomUtils.uuid());
}
clearAppend() {
this.appender.clear()
}
modifyAppender(idx: number) {
this.appender[idx][0] = RandomUtils.uuid();
}
}
<div dr-repeat="10"><div>#it#</div></div>
<div dr-repeat="$range(10, 20)"><div>#it#</div></div>
<div dr-repeat="$range(10, 20, 5)"><div>#it#</div></div>
<div dr-repeat="$range('10..5, 2')"><div>#it#</div></div>
<div dr-inner-text="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
<div dr-inner-html="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
click: <button dr-event-click="@this@.name = 'name' + new Date()">click</button> <br>
change: <input type="text" dr-event-change="@this@.name = $target.value"> <br>
input: <input type="text" dr-event-input="@this@.name = $target.value"> <br>
keyup: <input type="text" dr-event-keyup="@this@.name = $target.value"> <br>
...
keydown: <input type="text" dr-event-keydown="@this@.name = $target.value"><br>
submit: <form dr-event-submit="console.log($event); $event.preventDefault();"><input type="text"> <button type="submit">submit</button></form><br>
window-event-popstate: <input type="text" dr-window-event-popstate="alert(@this@.name)"><br>
<input dr-event:bind='eventName1, eventName2' dr-event="console.log('event', $params, $event)" type="text">
class data {
dictionary = {
name: 'visualkhh'
}
}
<div dr-this="@this@.dictionary">
${@this@.name}$
</div>
class data {
dictionary = {
name1: 'visualkhh1',
name2: 'visualkhh2',
name3: 'visualkhh3'
}
}
<home dr-this-property="@this@.dictionary" dr-on-init:arguments="[2,#this#]">
${this}$
</home>
dr-value: <input type="text" dr-value="@this@.office.name"> <br>
dr-value-link: <input type="text" dr-value-link="@this@.office.addr.street"> <br>
<textarea dr-attr="{rows: @this@.age/2, cols: @this@.age}"></textarea>
<div dr-attr="{wow: '123', good: 123444}"></div>
<div dr-attr="['wow=123', 'good=123444']"></div>
<div dr-attr="'wow=123, good=123444'"></div>
<div dr-class="{big: @this@.age > 50, red: @this@.age > 50}"></div>
<div dr-class="'big yellow ' + (@this@.age > 50 ? 'old' : 'young')"></div>
<div dr-class="['small', 'yellow']"></div>
<div dr-style="{fontSize: @this@.age + 'px'}"> style </div>
<div dr-style="{'font-size': '20px'}"> style</div>
<div dr-style="'font-size: ' + @this@.age +'px; margin: ' + @this@.age + 'px'"> style </div>
<div dr-style="['font-size: ' + @this@.age +'px', 'margin: ' + @this@.age + 'px']"> style </div>
<div dr-strip="true"><span>hello</span></div> <!-- output html : <span>hello</span> -->
<div dr-before="console.log('process before')" dr-after="console.log('process after')"></div>
<select dr-value-link="@this@.currentContry" dr-event-change="@this@.contryChange($event)">
<option dr-for-of="@this@.languages" dr-value="#it#.key" dr-complete="@this@.currentContry='defaultValue'">${#it#.title}$</option>
</select>
<body id="app">
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
name: <input name="name">
age: <input name="age">
<button type="submit">submit</button>
</form>
<script>
let data = {
form: {},
submit() {
console.log(this.form);
}
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
<!-- 💥 submit call -->
<!-- console: {name: 'name data', age: 'age data'} -->
<body id="app">
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
name: <input name="name">
age: <input name="age">
<button type="submit">submit</button>
</form>
<script>
const form = new FormValidator();
form.name = new NotEmptyValidator();
form.age = new NotEmptyValidator();
let data = {
form,
submit() {
if (this.form.valid()){
console.log('valid');
} else {
console.log('inValid');
}
}
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
validator
// Config
const config: Config = {
window
};
config.targetElements = [
DomRender.createComponent({type: Main, tagName: 'page-main', template: MainTemplate}),
DomRender.createComponent({type: Second, tagName: 'page-second', template: SecondTemplate}),
DomRender.createComponent({type: Detail, tagName: 'page-detail', template: DetailTemplate})
]
config.routerType = 'hash'; // 'hash' | 'path' | 'none';
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
<header>
<h1>examples header</h1>
<h2>${@this@.name}$</h2>
<div>
<div><button dr-event-click="$router.go('/')">main</button></div>
<div>
<button dr-event-click="$router.go('/second', {secondata: 555})">second</button>
<button dr-event-click="$router.go('/second/5')">second/1</button>
<button dr-event-click="$router.go('/second/wow')">second/2</button>
</div>
<div><button dr-event-click="$router.go('/detail/25?name=zzz')">detail</button></div>
</div>
</header>
<hr>
<main>
<page-main dr-if="$router.test('/')"></page-main>
<page-second dr-if="$router.test('/second')">1</page-second>
<page-second dr-if="$router.testRegexp('/second/[0-9]?$')">2</page-second>
<page-second dr-if="$router.testRegexp('/second/wow$')">wow</page-second>
<page-detail url='/detail/{id:[0-9]+}' dr-if="$router.test($attribute.url)" dr-on-create:callback="$component.routerData($router.getRouteData($attribute.url))" ></page-detail>
<div>
<button dr-event-click="@this@.plusCount()">${@this@.count}$ count pluse++</button>
</div>
</main>
<hr>
<footer>footer</footer>
export class Second implements OnCreateRender {
name = 'Second'
onCreateRender(data: CreatorMetaData): void {
console.log('----->', data.router)
}
}
import {RouteData} from 'dom-render/routers/Router';
import {OnCreateRender} from 'dom-render/lifecycle/OnCreateRender';
export class Detail implements OnCreateRender {
name = 'Detail';
onCreateRender(data: CreatorMetaData) {
console.log('routeData->', data);
}
routerData(routeData: RouteData) {
console.log('--------', routeData);
}
}
// RouteData type
type RouteData = {
path: string;
url: string;
data?: any;
searchParams: URLSearchParams;
pathData?: any;
}
export class Home implements OnProxyDomRender {
private channel?: Channel;
sendIndexMessage() {
const rtn = this.channel?.publish(Index, {
name: this.name,
age: this.age,
title: this.title
});
console.log('sendIndexMessage return value: ', rtn);
}
onProxyDomRender({messenger}: Config): void {
this.channel = messenger?.createChannel(Home);
}
}
class Index implements OnProxyDomRender {
onProxyDomRender({messenger}: Config): void {
messenger?.createChannel(this).filter((data) => (data.age ?? 0) > 5).subscribe((data) => {
this.rcvData = data;
return {data: 'good', action: 'actionGood'}
});
// messenger?.createChannel(this).subscribe((data) => {
// this.rcvData = data;
// return {data: 'good', action: 'actionGood'}
// });
}
}
const range = new Range(100,55, 10);
for (let data of new Range(100,55, 10)) {
console.log(data);
}
const rangeArray = new Range(100,55, 10).toArray();
const appender = new Appender<number>([1, 2]);
appender.push(3, 4)
for (const data of appender) {
console.log('----appender item--->', data);
}
{
name: 'dom-render'
onBeforeReturnSet: (name: string, value: any, fullpath: string[]) => {
console.log('set name-->', name, value, fullpath);
}
onBeforeReturnGet: (name: string, value: any, fullpath: string[]) => {
console.log('get name-->', name, value, fullpath);
}
}
exclude detect property: Config
export interface OnBeforeReturnSet {
onBeforeReturnSet(name: string, value: any, fullPath?: string[]): void;
}
export interface OnBeforeReturnGet {
onBeforeReturnGet(name: string, value: any, fullPath?: string[]): void;
}
all internal variables are managed by proxy. (DomRenderProxy)
exclude detect property: Config
Code base
// frezz
{name : Object.freeze({...})}
// Shield Object type: {[k: string]: any}
{name : new Shield()}
// DomRenderProxy Final
{name : DomRenderProxy.final({...})}
new DomRender.run(obj, target, {
scripts: {
concat: function (head: string, tail: string) {
return head + tail;
}
}
});
using script
const data = config.scripts.concat('head', 'tail')
<div>${$scripts.concat('head', 'tail')}</div>
<div dr-if="$scripts.concat('wow', 'good') === 'wowgood'"> is wowgood</div>
<body id="app">
${@this@.name}$
<h1>component</h1>
<profile dr-on-create:callback="$component.name='jhone'; $component.age=55;"><b>${#component#.details}$</b></profile>
<profile dr-on-create:callback="$component.name='cal'; $component.age=56;"><b>detail-2</b></profile>
<profile dr-on-create:callback="$component.name='rose'; $component.age=57;">
<profile dr-on-create:callback="$component.name='rose-sub'; $component.age=156;">
<b>${@this@.name}$</b>
</profile>
</profile>
<h3>component data link and detect</h3>
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-create:callback="$component.name='papa'; $component.age=58;">
<b>${@this@.name}$</b>
</Profile>
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-constructor:arguments="[1,2]">
<b>${@this@.name}$</b>
</Profile>
<button dr-event-click="@this@.name = new Date().toString();">change name</button>
<button dr-event-click="@this@.age = Date.now();">change age</button>
<button dr-event-click="@this@.toggle = !@this@.toggle;">change toggle</button>
<j1>component constructor, on-create, dr-on-create:callback</j1>
<home dr-constructor="[@this@.name, @this@.age, 'home welcom']" dr-on-create-arguments="{type: 'onCreate', data: 'datadata'}" dr-on-create:callback="$component.onInit('data')"></home>
<h1>scripts</h1>
<div>
${$scripts.concat('hello', 'tail')}$
</div>
<h1>attr</h1>
<button link="@this@.link">
link attribute
</button>
<h1>attrCallBack</h1>
<input id="callback" type="text" wow>
</body>
config.targetElements = [
DomRender.createComponent({type: Profile, template: ProfileTemplate}), // lazy loading format 'lazy://component/home.html'
DomRender.createComponent({type: Home, template: HomeTemplate, styles: HomeStyle})
]
config.targetAttrs = [
DomRender.createAttribute('link',
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
return obj;
},
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
const fag = window.document.createDocumentFragment();
if (attrValue) {
const n = element.cloneNode(true) as Element;
attrValue = ScriptUtils.eval(`return ${attrValue}`, obj)
n.addEventListener('click', () => {
location.href = attrValue;
});
fag.append(n);
}
return fag;
}
)
]
config.applyEvents = [
{
attrName: 'wow',
callBack: (e, a, o) => {
e.addEventListener('click', (event) => {
alert((event.target as any).value);
})
}
}
]
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
using component
<my-element dr-on-create:callback="$component.say();"></my-element>
<home value="${@this@.name}$" wow="${@this@.color}$">
${#component#.homeName}$
<home value="${#component#.homeName}$" wow="${#component#.homeColor}$" dr-component-name="sub_component" dr-component-inner-html-name="innerHTML">
${#sub_component#.homeName}$
</home>
</home>
lazy loading rollup config
copy({
targets: [
{
src: ['**/*.html', '**/*.css', '!node_modules/**/*.html', '!node_modules/**/*.css'], dest: 'dist',
rename: (name, extension, fullPath) => `${fullPath}`
},
{ src: 'assets', dest: 'dist' }
]
})
FAQs
html view template engine
The npm package dom-render receives a total of 8 weekly downloads. As such, dom-render popularity was classified as not popular.
We found that dom-render demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.